package se.bluebrim.usb.example;

import java.io.File;
import java.lang.reflect.Field;

import org.apache.commons.io.FilenameUtils;


/**
 * 
 * @author Jeff Friesen, Goran Stack
 *
 */
public class JavaUSBExampleApp
{

	public static void main(String[] args) {
		new JavaUSBExampleApp().run();
	}
	
	protected final void run()
	{
		setJavaLibraryPath(new File(getLibDirectoryRelativePath()).getPath());
		loadNativeLibraries();
	    enumerateHostControllers();
	}

	private void enumerateHostControllers() {
		// Enumerate all attached host controllers, outputting their names and
	      // the names of their root hubs. For each root hub, enumerate the device
	      // tree.

	      for (int i = 0; ; i++)
	      {
	           HCInfo hcinfo = JavaUSB.getHostControllerInfo (i);
	           if (hcinfo == null)
	               break;

	           System.out.println ("\nHost controller name = " + hcinfo.name);
	           System.out.println ("Root hub name = " + hcinfo.rootHubName);

	           enumerate (hcinfo.rootHubName, 1);
	      }
	}
	
	private void enumerate(String hubname, int depth) {
		Device[] devices = JavaUSB.getAttachedDevices(hubname);
		if (devices == null)
			return;

		for (int i = 0; i < devices.length; i++) {
			System.out.println();

			for (int j = 0; j < depth; j++)
				System.out.print(' ');

			System.out.println(devices[i]);

			for (int j = 0; j < depth; j++)
				System.out.print(' ');

			System.out.printf("Manufacturer = %s\n", devices[i].manufacturer);

			for (int j = 0; j < depth; j++)
				System.out.print(' ');

			System.out.printf("Product = %s\n", devices[i].product);

			for (int j = 0; j < depth; j++)
				System.out.print(' ');

			System.out.printf("Serial number = %s\n", devices[i].serialNumber);

			if (devices[i].isHub())
				enumerate(((Hub) devices[i]).hubName, depth + 1);
		}
	}
	
	private void loadNativeLibraries()
	{
		File nativeLibDir = new File(System.getProperty("java.library.path"));
		if (nativeLibDir.exists() && nativeLibDir.isDirectory())
		{
			File[] files = nativeLibDir.listFiles();
			for (File file : files) {
				System.loadLibrary(FilenameUtils.getBaseName(file.getName()));
			}
		} else
			throw new RuntimeException("Native library \"" + nativeLibDir.getPath() + "\" is missing");
	}

	protected String getLibDirectoryRelativePath() {
		return "native-lib";
	}

	/**
	 * <a href="http://blog.cedarsoft.com/2010/11/setting-java-library-path-programmatically/">Setting "java.library.path" programmatically</a>
	 */
	private void setJavaLibraryPath(String path) {
		System.setProperty("java.library.path", path);
		Field fieldSysPath;
		try {
			fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
		} catch (SecurityException e) {
			throw new RuntimeException(e);
		} catch (NoSuchFieldException e) {
			throw new RuntimeException(e);
		}
		fieldSysPath.setAccessible(true);
		try {
			fieldSysPath.set(null, null);
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}

}
